#define USING_NET_MAIL

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Net;

#if USING_NET_MAIL
using System.Net.Mail;
#else
using System.Web.Mail;
#endif

using CaveArt.Common.Util;

namespace CaveArt.Common.Util.IPMonitor
{
    public class IPMonitor
    {
        #region PUBLIC VALUES
        public const string CONFIGFILE = "IpMon.xml";
        public CaveArt.Common.Util.IPMonitor.Configuration config = null;
        public static string AppDataPath = String.Empty;
        #endregion

        #region PRIVATE VALUES
        private const string SMTPBASE = "http://schemas.microsoft.com/cdo/configuration/";
        private const int MAX_EMAILADDRESS = 80;
        private NTLog evtLog = new NTLog("Application", "Dynamic IP Monitor");
        #endregion

        private bool SendMail()
        {
            bool bRet = false;

            if (config != null &&
                Common.Util.NetUtil.ValidEmail(config.FromEmail, MAX_EMAILADDRESS))
            {
                string[] strEmails = config.ToEmail.Split(";,".ToCharArray());
                if (Common.Util.NetUtil.ValidEmail(strEmails, MAX_EMAILADDRESS))
                {
#if USING_NET_MAIL
                    MailMessage msg = new MailMessage();
                    SmtpClient client = new SmtpClient();

                    msg.From = new MailAddress(config.FromEmail);

                    foreach (string addr in strEmails)
                    {
                        msg.To.Add(new MailAddress(addr));
                    }

                    msg.Subject = config.EmailSubject;
                    msg.Body = "New IP address is " + config.ExternalIP;

                    client.Host = config.SMTPServerAddress;

                    try
                    {
                        // client.Credentials = 
                        client.Timeout = config.SMTPTimeout;
                        client.Port = config.SMTPPort;
                        client.EnableSsl = config.SMTPEnableSSL;

                        client.SendCompleted += new SendCompletedEventHandler(SmtpClient_SendCompleted);
                        client.Send(msg);
                        client.SendCompleted -= new SendCompletedEventHandler(SmtpClient_SendCompleted);
                    }
                    catch (Exception e)
                    {
                        this.evtLog.Log("Send e-mail error: " + e.Message);
                    }
                    finally
                    {
                        if (msg != null)
                        {
                            msg.Dispose();
                            msg = null;
                        }
                    }

#else
                    System.Web.Mail.MailMessage msg = null;

                    try
                    {
                        msg = new System.Web.Mail.MailMessage();

                        if (config.FromEmail.Length > 0)
                        {
                            int cdoBasic = 1;
                            int cdoSendUsingPort = 2;
                            
                            msg.Fields.Add(SMTPBASE + "smtpserver", config.UISMTPServerAddress);
                            msg.Fields.Add(SMTPBASE + "smtpserverport", 25);
                            msg.Fields.Add(SMTPBASE + "sendusing", cdoSendUsingPort);
                            msg.Fields.Add(SMTPBASE + "smtpauthenticate", cdoBasic);
                            msg.Fields.Add(SMTPBASE + "sendusername", config.UIUserID);
                            msg.Fields.Add(SMTPBASE + "sendpassword", config.UIPassword);
                        }

                        msg.To = config.ToEmail;
                        msg.From = config.FromEmail;
                        msg.Subject = config.EmailSubject;
                        msg.BodyEncoding = Encoding.UTF8;
                        msg.Body = "New IP address is " + config.ExternalIP;
                        System.Web.Mail.SmtpMail.SmtpServer = config.SMTPServerAddress;
                        System.Web.Mail.SmtpMail.Send(msg);
                        bRet = true;
                    }
                    catch (Exception e)
                    {
                        this.evtLog.Log("Send e-mail error: " + e.Message);
                    }
                    finally
                    {
                        if (msg != null)
                        {
                            msg = null;
                        }
                    }
#endif
                }
            }

            return bRet;
        }

        void SmtpClient_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            this.evtLog.Log("Notification email has been sent successfuly.");
        }

        public void CheckIP()
        {
            if (config != null && config.QueryURL.Length > 0)
            {
                string strNewIP = GetIP(config.QueryURL);
                if (strNewIP != config.ExternalIP)
                {
                    this.evtLog.Log("External IP address changed to: " + strNewIP);
                    config.ExternalIP = strNewIP;
                    SendMail();
                    SetConfig();
                }
            }
        }

        public void GetConfig()
        {
            if (config == null)
            {
                config = new Configuration();
                config.Default();
                TextReader r = null;
                XmlSerializer xs = null;

                try
                {
                    r = new StreamReader(AppDataPath + "\\" + CONFIGFILE);
                    xs = new XmlSerializer(typeof(CaveArt.Common.Util.IPMonitor.Configuration));
                    config = (Configuration) xs.Deserialize(r);

                }
                catch (XmlException xe)
                {
                    this.evtLog.Log("Read XML error: " + xe.Message);
                }
                catch (FileNotFoundException fe)
                {
                    this.evtLog.Log(AppDataPath + "\\" + CONFIGFILE + " not found. Using default values. " + fe.Message);
                }
                catch (Exception e)
                {
                    this.evtLog.Log("General error. Using default values. " + e.Message);
                }
                finally
                {
                    if (r != null)
                    {
                        r.Close();
                        r.Dispose();
                        r = null;
                    }
                    xs = null;
                }
            }
        }

        public void SetConfig()
        {
            XmlSerializer xs = null;
            TextWriter w = null;
            try
            {
                xs = new XmlSerializer(typeof(CaveArt.Common.Util.IPMonitor.Configuration));
                w = new StreamWriter(AppDataPath + "\\" + CONFIGFILE, false);
                XmlSerializerNamespaces xsn = new XmlSerializerNamespaces();
                xsn.Add(String.Empty, null);
                xs.Serialize(w, config, xsn);
                w.Close();
                xs = null;
            }
            catch (XmlException xe)
            {
                this.evtLog.Log("XML write settings error: " + xe.ToString() + ", " + xe.InnerException.ToString());
            }
            catch (Exception e)
            {
                this.evtLog.Log("XML Write settings error: " + e.ToString() + ", " + e.InnerException.ToString());
            }
        }

        private string GetIP(string url)
        {
            WebClient wc = new WebClient();
            string strIP = wc.DownloadString(url);
            strIP = (new Regex(@"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b")).Match(strIP).Value;
            wc.Dispose();
            return strIP;
        }

        #region NT Scheduler Setting (Not used currently)
#if USING_SCHEDULER
        /// <summary>
        /// Set Windows built-in Task Scheduler task to set up the IP monitoring schedules
        /// </summary>
        /// <returns>If the schedule set properly, return true. Otherwise return false.</returns>
        public bool SetScheduledTask()
        {
            // Get a ScheduledTasks object for the local computer.
            ScheduledTasks st = new ScheduledTasks();

            // Create a task
            Task t;
            try
            {
                t = st.CreateTask("Dynamic IP Monitor");
            }
            catch (ArgumentException)
            {
                Console.WriteLine("Task name already exists");
                return false;
            }

            // Fill in the program info
            t.ApplicationName = System.Reflection.Assembly.GetExecutingAssembly().Location;

            //TODO: Set Cmd Line Params as Configuration data
            t.Parameters = String.Empty;

            t.Comment = "Check and notify external IP address change.";

            // Set the account under which the task should run.
            t.SetAccountInformation(@"THEDOMAIN\TheUser", "HisPasswd");

            // Declare that the system must have been idle for ten minutes before 
            // the task will start
            t.IdleWaitMinutes = 0;

            // Allow the task to run for no more than 0 hours, 2 minutes.
            t.MaxRunTime = new TimeSpan(0, 5, 0);

            // Set priority to only run when system is idle/ normal.
            t.Priority = System.Diagnostics.ProcessPriorityClass.Normal;

            // Create a trigger to start the task every Sunday at 6:30 AM.
            t.Triggers.Add(new OnSystemStartTrigger());
            t.Triggers.Add(new DailyTrigger(18, 0));

            // Save the changes that have been made.
            t.Save();
            // Close the task to release its COM resources.
            t.Close();
            // Dispose the ScheduledTasks to release its COM resources.
            st.Dispose();

            return true;
        }
#endif
        #endregion
    }
}
